package model

import (
	"ginblog/utils/errormsg"
	"log"

	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)

type User struct {
	// g-orm提供的参数
	gorm.Model
	// 用户名
	Username string `gorm:"type: varchar(20); not null" json:"username" validator:"required, min=4, max=12" label:"用户名"`
	// 密码
	PassWord string `gorm:"type: varchar(200); not null" json:"password"  validator:"required, min=6, max=20" label:"密码"`
	// 角色
	Role int `gorm:"type: int; DEFAULT:2" json:"role" validator:"required, gte=2" label:"角色码"`
}

// 改变数据库中表的名字 防止复数形式
//func (u User) TableName() string {
//	return "user"
//}

// 查询用户是否存在
func CheckUser(name string) int {
	var user User
	// 查询数据库中的第一条数据
	db.Select("id").Where("username = ?", name).First(&user)
	if user.ID > 0 {
		return errormsg.ERROR_USERNAME_USED
	}
	return errormsg.SUCCESS
}

// 更新查询
func CheckUpUser(id int, name string) int {
	var user User
	// 查询数据库中的第一条数据
	db.Select("id, username").Where("username = ?", name).First(&user)
	if user.ID == uint(id) {
		return errormsg.SUCCESS
	}
	if user.ID > 0 {
		return errormsg.ERROR_USERNAME_USED
	}
	return errormsg.SUCCESS
}

// 新增用户 传入一个用户 然后返回对应的code提示 是否存在错误
func CreateUser(data *User) int {
	// 对用户密码加密 直接的方法
	// data.PassWord = ScrptPw(data.PassWord)
	// gorm 新增是create
	err := db.Create(&data).Error
	if err != nil {
		return errormsg.ERROR // 500
	}
	return errormsg.SUCCESS
}

// 查询单个用户
func GetUser(id int) (User, int) {
	var user User
	err := db.Limit(1).Where("ID = ?", id).Find(&user).Error
	if err != nil {
		return user, errormsg.ERROR
	}
	return user, errormsg.SUCCESS
}

// 查询用户列表
func GetUsers(username string, pageSize int, pageNum int) ([]User, int64) {
	var users []User
	var total int64
	//var user User
	//db.Model(&user).Count(&total)
	// 这里是用户名查询
	if username != "" {
		db.Select("id,username,role").Where(
			"username LIKE ?", username+"%",
		).Limit(pageSize).Offset((pageNum - 1) * pageSize).Find(&users)
		db.Model(&users).Where(
			"username LIKE ?", username+"%",
		).Count(&total)
		return users, total
	}
	// 传入的pageSize为-1 则不需要 分页 同理 pageNum 传入为-1 也不需要分页
	// 这里是通过limit分页获取用户列表
	db.Select("id,username,role").Limit(pageSize).Offset((pageNum - 1) * pageSize).Find(&users)
	db.Model(&users).Count(&total)
	if err == gorm.ErrRecordNotFound {
		return nil, 0
	}
	return users, total
}

// 编辑用户
func EditUser(id int, data *User) int {
	// 使用map传参来更新数据库中的数据 因为gorm使用结构体更新数据 不会更新0值
	var maps = make(map[string]interface{})
	maps["username"] = data.Username
	maps["role"] = data.Role
	// 更新的语句
	err := db.Model(&User{}).Where("id = ?", id).Updates(&maps).Error
	if err != nil {
		return errormsg.ERROR
	}
	return errormsg.SUCCESS
}

// 删除用户
func DeleteUser(id int) int {
	var user User
	err := db.Where("id = ?", id).Delete(&user).Error
	if err != nil {
		return errormsg.ERROR
	}
	return errormsg.SUCCESS
}

// 密码加密 多种方法 MD5简单 bcrypt中等 scrypt高级
// 钩子函数用来加密 gorm框架在保存用户的时候 会先执行这一步 （实现这个接口）
func (u *User) BeforeCreate(_ *gorm.DB) (err error) {
	u.PassWord = ScrptPw(u.PassWord)
	u.Role = 2
	return nil
}

func (u *User) BeforeUpdate(_ *gorm.DB) (err error) {
	u.PassWord = ScrptPw(u.PassWord)
	return nil
}

func ScrptPw(password string) string {
	// scrypt加密
	//const KeyLen = 10
	//	//salt := []byte{5, 8, 9, 45, 36, 12, 78, 99}
	//	//HashPw, err := scrypt.Key([]byte(password), salt, 16384, 8, 1, KeyLen)
	const cost = 10
	HashPw, err := bcrypt.GenerateFromPassword([]byte(password), cost)
	if err != nil {
		log.Fatal(err)
	}
	return string(HashPw)
}

// 登录验证
func CheckLogin(username, password string) (User, int) {
	var user User
	var PasswordErr error
	db.Where("username = ?", username).First(&user)
	// 密码对比
	PasswordErr = bcrypt.CompareHashAndPassword([]byte(user.PassWord), []byte(password))
	if user.ID == 0 {
		return user, errormsg.ERROR_USER_NOT_EXIST
	}
	if PasswordErr != nil {
		return user, errormsg.ERROR_PASSWORD_WRONG
	}
	if user.Role != 1 {
		return user, errormsg.ERROR_USER_NO_RIGHT
	}
	return user, errormsg.SUCCESS
}

// 修改密码
func ChangePw(id int, data *User) int {
	err = db.Select("pass_word").Where("id = ?", id).Updates(&data).Error
	if err != nil {
		return errormsg.ERROR
	}
	return errormsg.SUCCESS
}

// 前台登录验证
func CheckLoginFront(username, password string) (User, int) {
	var user User
	var PasswordErr error
	db.Where("username = ?", username).First(&user)
	PasswordErr = bcrypt.CompareHashAndPassword([]byte(user.PassWord), []byte(password))
	if user.ID == 0 {
		return user, errormsg.ERROR_USER_NOT_EXIST
	}
	if PasswordErr != nil {
		return user, errormsg.ERROR_PASSWORD_WRONG
	}
	return user, errormsg.SUCCESS
}
